class Vue{
    constructor(options) {
        //this.$el $data
        this.$el = options.el;  //模板
        this.$data = options.data || {}; //数据

        if(this.$el){
            //数据劫持
            new Observer(this.$data);
            //进行模板编译，最终把数据展示到页面上
            new Complie(this.$el,this);
        }
    } 
}

//发布订阅
class Dep{
    constructor(){
        this.watchers = [];
    }

    addWatch(watch){
        //订阅观察者
        this.watchers.push(watch);
    }

    notify(){
        //发布
        this.watchers.forEach(watch => watch.update())
    }
}

//观察者模式   观察者  被观察者
class Watcher{
    constructor(vm,expr,cb){
        this.vm = vm;
        this.expr = expr;
        this.cb = cb;
        //默认先存放老值
        this.oldValue = this.get();
    }

    get(){
        Dep.target = this;
        let value = compileUtil.getVal(this.expr,this.vm);
        Dep.target = null;
        return value;
    }

    update(){ //更新操作  数据变化后  会调用观察者的update方法
        let newVal = compileUtil.getVal(this.expr,this.vm);
        if(newVal != this.oldValue){
            this.cb(newVal);
        }
    }
}

// vm.$watch(vm,'school.name',(newValue) => {

// })


//实现数据劫持

class Observer{
    constructor(data){
        this.observer(data);
    }

    observer(data){
        //如果是对象才观察
        if(data && typeof data === 'object'){
            for(let key in data){
                this.defineReactive(data,key,data[key]);
            }
           
        }
    }

    defineReactive(obj,key,value){
        this.observer(value);
        let dep = new Dep();
        Object.defineProperty(obj,key,{
            get(){
                Dep.target && dep.addWatch(Dep.target);
                return value
            },
            set:(newValue)=>{
                if(newValue != value){
                    this.observer(value);
                    value = newValue;
                    //通知每个watcher
                    dep.notify();
                }
            }
        })
    }
}

compileUtil = {
    getVal(expr,vm){
        return expr.split('.').reduce((data,current) => {
            return data[current]
        },vm.$data)
    },
    setValue(vm,expr,data){
        expr.split('.').reduce((prev,current,index,arr) => {
            if(index === arr.length-1){
                return prev[current] = data
            }
            return prev[current]
        },vm.$data)
    },
    model(node,expr,vm){
        let fn = this.update.modelUpdate;
        let value = this.getVal(expr,vm);
        new Watcher(vm,expr,(newVal) => {
            fn(node,newVal);
        })
        fn(node,value);
        node.addEventListener('input',e=>{
            let data = e.target.value;
            this.setValue(vm,expr,data);
        })
    },
    text(node,expr,vm){
        let content = expr.replace(/\{\{(.+?)\}\}/g,(...args) => {
            new Watcher(vm,args[1],(newValue)=>{
                fn(node,newValue)
            })
            return this.getVal(args[1],vm);
        });

        let fn = this.update.textUpdate;
        fn(node,content);
    },
    update: {
        modelUpdate(node,valueData){// <input /> "八维教育"
            //<input value="八维教育" /> 
            node.value = valueData
        },
        textUpdate(node,contentData){
            // {{school}} = “八维教育”
            node.textContent = contentData
        }
    }
}

//编译
class Complie{
    constructor(el,vm){
        this.vm = vm;
        //元素节点  字符串
        this.el = this.isElementNode(el) ? this.el : document.querySelector(el);

        //把所有的元素放到内存中
        let fragment = this.nodeToFragment(this.el);

        // console.log(fragment);

        //编辑模板，数据
        this.compile(fragment);

        this.el.appendChild(fragment)
    }

    //判断属性是否是指令
    isDirective(attr){
        // console.log(attr)
        return attr.startsWith('v-')
    }

    //编译元素节点
    compileElement(node){
        let attributes = node.attributes;
        [...attributes].forEach(attr => {
            let {name,value:expr} = attr;
            if(this.isDirective(name)){
                //是指令才替换  model html text
                // console.dir(attr);
                let [,directive] = name.split('-');
                compileUtil[directive](node,expr,this.vm);
            }
        })
    }

    //编译文本节点
    compileText(node){
        // console.log(node);
        let content = node.textContent;
        if(/\{\{(.+?)\}\}/.test(content)){
            compileUtil['text'](node,content,this.vm);
        }
    }


    //编译的方法
    compile(node){
        // console.log(node.childNodes)
        let childNodes = node.childNodes;
        [...childNodes].forEach(child => {
            // console.log(child)
            if(this.isElementNode(child)){
                //元素节点
                this.compileElement(child);
                this.compile(child);
            }else{
                //文本节点
                this.compileText(child);
            }
        })
    }

    nodeToFragment(node){
        //createDocumentFragment:创建一个新的空白的文档片段
        let fragment = document.createDocumentFragment();

        let firstChild;
        // console.dir(node);
        while(firstChild = node.firstChild){
            // console.log("======");
            fragment.appendChild(firstChild);
        }
        return fragment
    }

    isElementNode(node){
        //nodeType返回节点的类型  1:元素节点  2：属性节点  3：文本节点  8：注释节点
        return node.nodeType === 1
    }
}